package online

import (
	"errors"
	"fmt"
	"server/common"
	"time"

	"github.com/mitchellh/mapstructure"
	"github.com/sirupsen/logrus"
)

// -------------------------------------------------------------------------------------------------
//
// -------------------------------------------------------------------------------------------------
func (s *OnlineService) sendCallReqErrorResponse(rid string, reqData common.CallMakeReqData,
	code int, msg string, connId string) {
	errorRes := common.Message{
		Cmd: common.CALL_MAKE_RES,
		Rid: rid,
		Data: common.ResMsgData{
			Code: code,
			Msg:  msg,
			Data: common.CallMakeResData{
				CallType: reqData.CallType,
				CallId:   "",
				CalleeId: reqData.CalleeId,
				CallerId: reqData.CallerId,
			},
		},
	}

	s.dispatcher.Dispatch(errorRes, s.serviceInfo, common.AccessServiceInfo, connId)
}

// -------------------------------------------------------------------------------------------------
//
// -------------------------------------------------------------------------------------------------
func (s *OnlineService) handleP2pCall(rid string, from common.ServiceInfo, caller *UserInfo,
	callee *UserInfo) error {
	common.Logger(common.SERVICE_TYPE_ONLINE, rid).WithFields(logrus.Fields{
		"from":   from,
		"callee": common.ToJson(callee),
	}).Info("call handleP2PCall")

	// 1. 构建 call_id
	callId := common.GenerateRandomStr(16)

	// 2. 构建 call notify data
	notifyData := common.CallMakeNotifyData{
		CallType: "p2p",
		CallId:   callId,
		CallerId: caller.UserId,
		CalleeId: callee.UserId,
	}

	// 3. 发送 call notify 给 callee
	if err := s.dispatcher.Dispatch(
		common.Message{
			Cmd:  common.CALL_MAKE_NOTIFY,
			Rid:  rid,
			Data: notifyData,
		},
		s.serviceInfo,
		callee.ServiceInfo,
		callee.ConnId,
	); err != nil {
		return errors.New("failed to send call notify: " + err.Error())
	}

	// 4. 构建 call data
	callData := CallData{
		CallType:  "p2p",
		CalleeId:  callee.UserId,
		CallId:    callId,
		CallerId:  caller.UserId,
		CallState: Calling,
		Timestamp: time.Now().Unix(),
		From:      from,
	}

	// 5. 添加 call 到 call manager
	if err := s.callMgr.AddCall(callId, callData); err != nil {
		return fmt.Errorf("failed to add call: %v", err)
	}

	return nil
}

// -------------------------------------------------------------------------------------------------
//
// -------------------------------------------------------------------------------------------------
func (s *OnlineService) handleSfuCall(rid string, from common.ServiceInfo, caller *UserInfo,
	callee *UserInfo) error {
	common.Logger(common.SERVICE_TYPE_ONLINE, rid).WithFields(logrus.Fields{
		"from":   from,
		"callee": common.ToJson(callee),
	}).Info("call handleSfuCall")

	// 1. 构建 call_id
	callId := common.GenerateRandomStr(16)

	// 2. 构建 call notify data
	notifyData := common.CallMakeNotifyData{
		CallType: "p2sp",
		CallId:   callId,
		CallerId: caller.UserId,
		CalleeId: callee.UserId,
	}

	// 3. 发送 call notify 给 callee
	if err := s.dispatcher.Dispatch(
		common.Message{
			Cmd:  common.CALL_MAKE_NOTIFY,
			Rid:  rid,
			Data: notifyData,
		},
		s.serviceInfo,
		callee.ServiceInfo,
		callee.ConnId,
	); err != nil {
		return errors.New("failed to send call notify: " + err.Error())
	}

	// 4. 构建 call data
	callData := CallData{
		CallType:  "p2sp",
		CalleeId:  callee.UserId,
		CallId:    callId,
		CallerId:  caller.UserId,
		CallState: Calling,
		Timestamp: time.Now().Unix(),
		From:      from,
	}

	// 5. 添加 call 到 call manager
	if err := s.callMgr.AddCall(callId, callData); err != nil {
		return errors.New("failed to add call: " + err.Error())
	}

	return nil
}

// -------------------------------------------------------------------------------------------------
//
// -------------------------------------------------------------------------------------------------
func (s *OnlineService) checkCallMakeReqData(data common.CallMakeReqData) error {
	if data.CalleeId == "" {
		return errors.New("callee_id is required in call make request")
	}

	// Check if callee exists
	if !s.userMgr.UserExists(data.CalleeId) {
		return errors.New("callee_id does not exist")
	}

	// Check if call type is valid
	if data.CallType != "p2p" && data.CallType != "p2sp" {
		return errors.New("invalid call_type, must be 'p2p' or 'p2sp'")
	}

	return nil
}

// -------------------------------------------------------------------------------------------------
//
// -------------------------------------------------------------------------------------------------
func (s *OnlineService) handleCallMakeReq(msg common.Message, from common.ServiceInfo,
	connId string) error {
	LOG(msg.Rid).WithFields(logrus.Fields{
		"msg":  common.ToJson(msg),
		"from": from,
	}).Info("call handleCallRequest")

	var reqData common.CallMakeReqData
	if err := mapstructure.Decode(msg.Data, &reqData); err != nil {
		LOG(msg.Rid).WithError(err).Error("invalid request data format")
		s.sendCallReqErrorResponse(msg.Rid, reqData, 1, "invalid request data format", connId)
		return nil
	}

	if err := s.checkCallMakeReqData(reqData); err != nil {
		LOG(msg.Rid).WithError(err).Error("call make request validation failed")
		s.sendCallReqErrorResponse(msg.Rid, reqData, 1, err.Error(), connId)
		return nil
	}

	caller := s.userMgr.GetUserByConnId(connId)
	if caller == nil {
		LOG(msg.Rid).Error("caller not found")
		s.sendCallReqErrorResponse(msg.Rid, reqData, 1, "caller not found", connId)
		return nil
	}

	if caller.UserId == reqData.CalleeId {
		LOG(msg.Rid).Error("caller and callee cannot be the same user")
		s.sendCallReqErrorResponse(msg.Rid, reqData, 1, "caller and callee cannot be the same user",
			connId)
		return nil
	}

	callee := s.userMgr.GetUserByUserId(reqData.CalleeId)
	if callee == nil {
		LOG(msg.Rid).Errorf("callee not found: %s", reqData.CalleeId)
		s.sendCallReqErrorResponse(msg.Rid, reqData, 1, "callee not found", connId)
		return nil
	}

	switch reqData.CallType {
	case "p2p":
		if err := s.handleP2pCall(msg.Rid, from, caller, callee); err != nil {
			s.sendCallReqErrorResponse(msg.Rid, reqData, 1, err.Error(), connId)
		}
	case "p2sp":
		if err := s.handleSfuCall(msg.Rid, from, caller, callee); err != nil {
			s.sendCallReqErrorResponse(msg.Rid, reqData, 1, err.Error(), connId)
		}
	default:
		LOG(msg.Rid).Errorf("unknown call type: %s", reqData.CallType)
	}

	return nil
}
